/* spearl/include/spearl/spearl.h 
 * 
 * This file is part of spearl. 
 * 
 * Spearl is free software: you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by 
 * the Free Software Foundation, either version 3 of the License, or 
 * (at your option) any later version. 
 * 
 * Spearl is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
 * GNU General Public License for more details. 
 * 
 * You should have received a copy of the GNU General Public License 
 * along with spearl. If not, see <https://www.gnu.org/licenses/>
 */ 




#ifndef __SPEARL_SPEARL_H
#define __SPEARL_SPEARL_H

#include <spearl/sys.h>
#include <spearl/compiler.h>
#include <spearl/klist.h>

/* user should never use a variable or function that marks with __locally. 
 * oppositely, they can / should / must use __visible things. 
 * 
 * for __locally variables and functions, we use the prefix 'spl' and for 
 * __visibles, we use 'spearl' and give each of them will be well-commented. 
 *
 * for unmarked symbols (exempli grandia est spearl_object, id didn't marked 
 * at all), users can use them carefully, but shouldn't modify or depends on 
 * its behavior. */

#define __locally
#define __visible

extern char __locally *spl_input;
extern int __locally spl_input_size;

extern struct spearl_object __visible root_object;

extern char __visible lbracket, rbracket;
static inline __visible void spearl_setbracket_chars(char l, char r) {
	lbracket = l, rbracket = r;
}

static inline __visible void spearl_setbracket(char *s) {
	spearl_setbracket_chars(s[0], s[1]);
}

#define MCALL_MNAME 			'#'
#define MCALL_MARGS 			'~'
#define MCALL_MDEST 			'@'

struct spearl_word {
	/* pointer to somewhere in spl_input, doesn't terminate with zero */
	char 				*p;
	/* length of string, by strlen without + 1 */
	int 				len;
	/* the head is in spearl_list */
	struct spl_klist_node 		node;
};

extern struct spl_klist_node __visible wordlist;
#define word_of_node(n) 	struct_of_node(n, struct spearl_word)

/* the ';' last is not required, just to make it more beautiful. */
spl_make_alloc(struct spearl_word, word);

struct spearl_list {
	struct spl_klist_node 		node;
	struct spl_klist_node 		objs;
	int 				nobjs;
};

#define list_of_node(n) 		struct_of_node(n, struct spearl_list)
spl_make_alloc(struct spearl_list, list);

enum spl_object_type {
	WORD, 
	LIST, 
};

struct spearl_object {
	enum spl_object_type 		type;
	struct spl_klist_node 		node;

	union {
		struct spearl_word 	*word;
		struct spearl_list 	*list;
	};
};

#define object_of_node(n) 		struct_of_node(n, struct spearl_object)
spl_make_alloc(struct spearl_object, object);

/* status of scanner, describes the current status. IN if is in 
 * a quote and OUT if it's OUT of a quote. */

enum spl_state {
	IN, 
	OUT
};

#define spl_state_name() 		__state_tmp
#define spl_state_level_name()		__state_level__tmp
#define spl_make_state() 		enum spl_state spl_state_name() __unused = OUT; \
	int spl_state_level_name() __unused = 0;
#define spl_current_state() 		spl_state_name()
#define spl_set_state(s) 		spl_current_state() = s
#define spl_current_state_level() 	spl_state_level_name()
#define spl_state_in() 			(spl_current_state() == IN)
#define spl_state_out() 		(spl_current_state() == OUT)
#define spl_state_left() 		spl_current_state_level()++
#define spl_state_right() 		if (!--spl_current_state_level()) spl_set_state(OUT)

struct spearl_mcall_arg {
	enum spl_object_type 		type;
	struct spearl_object 		*object;

	union {
		/* zero-terminated */

		char 			*p;
		struct spearl_list 	*list;
	};

	struct spl_klist_node 		node;
};

spl_make_alloc(struct spearl_mcall_arg, arg);

struct spearl_mcall {
	struct spearl_list 		*list;
	struct spearl_object 		*dest;
	struct spearl_object 		*mname;
	struct spl_klist_node 		args;
};

spl_make_alloc(struct spearl_mcall, mcall);

enum spl_macro_type {
	USER, 
	BUILTIN, 
};

typedef struct spearl_list *spearl_builtin_fn_t (struct spearl_mcall *);

struct spearl_builtin {
	char 				*name;
	spearl_builtin_fn_t 		*do_builtin;
};

struct spearl_macro {
	enum spl_macro_type 		type;
	char 				*name;

	union {
		struct {
			struct spearl_object 
					*eval,  
					*ins;
		};

		struct spearl_builtin 	builtin;
	};

	struct spl_klist_node 		node;
};

#define macro_of_node(n) 		struct_of_node(n, struct spearl_macro)

spl_make_alloc(struct spearl_macro, macro);
extern struct spl_klist_node __visible spl_macros;

/* initialize the library, or reset */
int __visible spearl_init(void);
/* join a string to spl_input and scan, join into spearl_list */
int __visible spearl_join(char *s);
/* define a new builtin */
struct spearl_macro *spearl_define_builtin(char *name, spearl_builtin_fn_t fn);

int __locally spl_builtin_init(void);
int expand_builtin_macro(struct spearl_macro *, struct spearl_mcall *, struct spl_klist_node *);

#endif

